package com.cmc6.user.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.cmc6.common.constant.BaseConstant;
import com.cmc6.common.exception.BaseBizCodeEnum;
import com.cmc6.common.mapper.UserIdMapper;
import com.cmc6.common.model.entity.UserIdDO;
import com.cmc6.common.util.ApiResult;
import com.cmc6.common.util.SmsTencentUtil;
import com.cmc6.user.exception.BizCodeEnum;
import com.cmc6.user.mapper.UserInfoMapper;
import com.cmc6.user.mapper.UserLoginMapper;
import com.cmc6.user.mapper.UserSecurityMapper;
import com.cmc6.user.model.dto.UserLoginInsertOrUpdateDTO;
import com.cmc6.user.model.entity.UserInfoDO;
import com.cmc6.user.model.entity.UserLoginDO;
import com.cmc6.user.model.entity.UserSecurityDO;
import com.cmc6.user.service.UserRegHelperService;
import com.cmc6.user.service.UserService;
import com.cmc6.user.util.PasswordConverterUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class UserRegHelperServiceImpl implements UserRegHelperService {

    @Resource
    UserIdMapper userIdMapper;
    @Resource
    UserInfoMapper userInfoMapper;
    @Resource
    UserSecurityMapper userSecurityMapper;
    @Resource
    UserLoginMapper userLoginMapper;
    @Resource
    RedisTemplate<String, String> redisTemplate;
    @Resource
    RedissonClient redissonClient;
    @Resource
    UserService userService;

    /**
     * 基础注册，注意：所有注册，都必须调用此方法
     */
    @Override
    public UserSecurityDO regBase(UserLoginInsertOrUpdateDTO dto, boolean checkPasswordBlank) {

        // 插入 用户主表
        UserIdDO userIdDO = new UserIdDO();
        userIdDO.setUuid(IdUtil.simpleUUID());
        userIdMapper.insert(userIdDO);

        // 插入 用户基本信息表
        UserInfoDO userInfoDO = new UserInfoDO();
        userInfoDO.setUserId(userIdDO.getId());
        userInfoDO.setNickname(getDefaultNickname());
        userInfoDO.setPersonalStatement("");
        userInfoDO.setAvatarUrl("");
        userInfoMapper.insert(userInfoDO);

        // 插入 用户安全以及状态相关表
        UserSecurityDO userSecurityDO = new UserSecurityDO();
        userSecurityDO.setCreateId(userIdDO.getId());
        userSecurityDO.setUpdateId(userIdDO.getId());
        userSecurityDO.setUserId(userIdDO.getId());
        userSecurityDO.setJwtSecretSuf(IdUtil.simpleUUID());
        userSecurityMapper.insert(userSecurityDO);

        // 插入 用户基本登录表
        UserLoginDO userLoginDO = new UserLoginDO();
        userLoginDO.setUserId(userSecurityDO.getUserId());
        userLoginDO.setEmail(dto.getEmail());
        userLoginDO.setPassword(PasswordConverterUtil.converter(dto.getPassword(), checkPasswordBlank));
        userLoginDO.setPhone(dto.getPhone());
        userLoginMapper.insert(userLoginDO);

        return userSecurityDO;
    }

    /**
     * 获取默认的用户名
     * 备注：不使用邮箱的原因，因为邮箱不符合 用户昵称的规则：只能包含中文，数字，字母，下划线，长度2-20
     */
    public static String getDefaultNickname() {
        return "用户昵称" + RandomUtil.randomString(6).toUpperCase();
    }

    /**
     * 通过 redis，检查 验证码，备注：请配合 分布式锁
     */
    @Override
    public void checkCodeByRedis(String key, String code) {

        if (StrUtil.isBlank(key) || StrUtil.isBlank(code)) {
            ApiResult.error(BaseBizCodeEnum.PARAMETER_CHECK_ERROR);
        }

        // 从 redis中根据 key，拿到验证码（随机值），redis中的格式为：{ key : code}
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        String redisCode = ops.get(key);

        // 如果不存在验证码，操作失败：请先获取验证码
        if (StrUtil.isBlank(redisCode)) {
            ApiResult.error(BizCodeEnum.PLEASE_GET_THE_VERIFICATION_CODE_FIRST);
        }
        // 如果验证码不匹配，则提示：验证码有误，请重试
        if (!code.equalsIgnoreCase(redisCode)) {
            ApiResult.error(BizCodeEnum.CODE_IS_INCORRECT);
        }

        redisTemplate.delete(key); // 验证通过，删除redis中的验证码

    }

    /**
     * 给手机发送：验证码
     * type：1 注册 2 登录 3 忘记密码 4 修改手机 5 绑定手机
     */
    @Override
    public String phoneSendCode(String phone, int type) {

        // 增加分布式锁
        // 获取一把锁，只要锁名字一样，就是同一把锁，即：同一时间只能有一个手机进行 注册/修改
        RLock lock = redissonClient.getLock(BaseConstant.PRE_REDISSON + BaseConstant.PRE_LOCK_PHONE_REG_CODE + phone);
        lock.lock();

        try {

            // 备注：这里不判断 type == 2的原因：因为现在的登录，是有账号直接登录，没有账号则自动注册，并登录
            boolean phoneExist = userService.checkPhoneExist(phone, null);
            if (type == 1 || type == 4 || type == 5) {
                if (phoneExist) {
                    ApiResult.error(BizCodeEnum.PHONE_HAS_BEEN_REGISTERED);
                }
            } else if (type == 3) {
                if (!phoneExist) {
                    ApiResult.error(BizCodeEnum.PHONE_NUMBER_DOES_NOT_EXIST);
                }
            }

            String code = null;
            if (type == 1) {
                code = SmsTencentUtil.sendReg(phone);
            } else if (type == 2) {
                code = SmsTencentUtil.sendLogin(phone);
            } else if (type == 3) {
                code = SmsTencentUtil.sendForgotPassword(phone);
            } else if (type == 4) {
                code = SmsTencentUtil.sendUpdate(phone);
            } else if (type == 5) {
                code = SmsTencentUtil.sendBind(phone);
            }

            if (code == null) {
                ApiResult.error("操作失败：type不合法");
            }

            // 保存到redis中，格式：{PRE_LOCK_PHONE_REG_CODE + phone : code}
            // 设置10分钟过期
            redisTemplate.opsForValue()
                .set(BaseConstant.PRE_LOCK_PHONE_REG_CODE + phone, code, BaseConstant.MINUTE_10_EXPIRE_TIME,
                    TimeUnit.MILLISECONDS);

            return "发送成功";

        } finally {
            // 解除分布式锁
            lock.unlock();
        }

    }

}
